ThienTanBasicForm = Ext.extend(Ext.form.BasicForm, {
    
    waitMsgTarget : true,
    
    trackResetOnLoad : true,
    
    constructor: function(el, config){
    	this.addEvents(
                /**
                 * @event infoMessage
                 * Fires when action is given a info message. 
                 * @param {msg} JSON array of message
                 */
                'infoMessage',
                
                /**
                 * @event popupconcurrent
                 * Fires when action is given a concurrent error. 
                 */
                'popupconcurrent',
				
				/**
				 * @event popupcritical
				 * Fires when action is given a critical error.
				 */
				 'popupcritical'
				 
        );
    	
    	this.tables = new Ext.util.MixedCollection(false, function(o) {
            return o.getItemId();
        });
    	
    	this.comboBoxs = new Ext.util.MixedCollection(false, function(o) {
            return o.getItemId();
        });
    	
    	ThienTanBasicForm.superclass.constructor.call(this);
    },
    
    /**
     * @override from Ext.form.BasicForm
     */
    beforeAction : function(action){
        // Call HtmlEditor's syncValue before actions
        this.items.each(function(f){
            if(f.isFormField && f.syncValue){
                f.syncValue();
            }
        });
        var o = action.options;
        if(o.waitMsg){
            if(this.waitMsgTarget === true){
                this.el.mask(o.waitMsg, 'x-mask-loading');
                if (o.scope.autoLoadData) {
	                if (o.scope) {
	                	o.scope.setTitleState('loading');
	                }
                }
            }else if(this.waitMsgTarget){
                this.waitMsgTarget = Ext.get(this.waitMsgTarget);
                this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
            }else{
                Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle);
            }
        }
    },
    
    /**
     * @override from Ext.form.BasicForm
     */
    afterAction : function(action, success){
        this.activeAction = null;
        var o = action.options;
        if(o.waitMsg){
            if(this.waitMsgTarget === true){
                this.el.unmask();
                if (o.scope.autoLoadData) {
	                if (o.scope) {
	                	if (o.scope.previousIconCls) {
	                		o.scope.setTitle(o.scope.title? o.scope.title : '', o.scope.previousIconCls);
	                	} else {
	                		o.scope.setTitleState();
	                	}
	                }
                }
            }else if(this.waitMsgTarget){
                this.waitMsgTarget.unmask();
            }else{
                Ext.MessageBox.updateProgress(1);
                Ext.MessageBox.hide();
            }
        }
        if(success){
            if(o.reset){
                this.reset();
            }
            //start customize : Fire infoMessage event to display information
            if(o.resetAfterSuccess) {
            	this.resetAfterSuccess();
            }
            if(action.result.msg) {
                this.fireEvent('infoMessage', action.result.msg, this);
            }
            //end customize
            Ext.callback(o.success, o.scope, [this, action]);
            
            this.fireEvent('actioncomplete', this, action);
        }else{
            var msg;
            var flagInfoMsg = true;
            //start customize : Fire infoMessage event to display information
            switch (action.failureType) {
                case Ext.form.Action.CLIENT_INVALID:
                    msg = { action : 'error', msg : '' };
                    msg.msg = lang.msg.responseClientInvalidError;
                    break; 
                case Ext.form.Action.CONNECT_FAILURE:
                    msg = { action : 'error', msg : '' };
                    msg.msg = lang.msg.responseConnectFailure;
                    break;
                case Ext.form.Action.LOAD_FAILURE:
                    msg = { action : 'error', msg : '' };
                    msg.msg = lang.msg.responseLoadFailure;
                    break;
                case Ext.form.Action.SERVER_INVALID:
                	if (action.result.msg && action.result.msg.type === 'org.springframework.dao.OptimisticLockingFailureException') {
                		flagInfoMsg = false;
                		this.fireEvent('popupconcurrent', this);
                	} else if (action.result.msg && action.result.msg.type === 'critical') {
						flagInfoMsg = false;
						this.fireEvent('popupcritical', this);
					} else {
                		msg = { action: 'error', msg: lang.msg.unknownError };
                	}
                    break;
            }
            
            if (flagInfoMsg) {
            	msg.msg = msg.msg != '' ? msg.msg : lang.msg.responseGeneralFailure;
            	this.fireEvent('infoMessage', msg, this);
            }
            //end customize
            Ext.callback(o.failure, o.scope, [this, action]);
            this.fireEvent('actionfailed', this, action);
            var obj = o.scope;
            if (obj && obj.tabEl) {
                if (obj.autoLoadData) {
                    obj.setTitle(obj.title? obj.title : '');
                    var tabEl = Ext.fly(obj.tabEl);
                    tabEl.addClass('error-icon');
                    // For CSS2, we remove 'error-temp' class to remove error-icon
                    // in active tab. 'error-temp' was added in Ext.oxygen.TabBar.itemTpl
                    // when tabchange if current browser is IE7 or IE8 
                    if(Ext.isIE7 || Ext.isIE8) {
                        tabEl.removeClass('error-temp');
                    }
                }
            }
        }
    },
    
    /**
     * @override from Ext.form.BasicForm
     * The errors parameter always is array. 
     */
    markInvalid : function(errors){
       for(var i = 0, len = errors.length; i < len; i++){
            var fieldError = errors[i];
            var f = this.findField(fieldError.id);
            if(f){
                f.markInvalid(fieldError);
            }
        }
        return this;
    },
    
    /**
     * @private 
     * searches for the table corresponding to the given id.
     */
    findTable : function(id) {
        var table = this.tables.get(id);
        return table;
    },
    
    /**
     * @override from Ext.form.BasicForm
     */
    setValues : function(values){
        //load data for all comboBoxs
        var idc;
        var combos = values.comboData; 
        if(combos) {
            for(idc in combos) {
                var combo = this.comboBoxs.get(idc);
                if(combo) {
                	if (combo instanceof Ext.oxygen.RemoteSuggestField) {
                		var len = values.comboData[idc].length;
        			    var cacheData = [];
        		        for (var i = 0; i < len; i++){
        		        	var item = new Ext.data.Record(values.comboData[idc][i]);
        		            combo.cacheData.push(item);
        		        }
                	} else {
                		combo.store.loadData(values.comboData[idc]);
                	}
                }
            }
        }
        //load data for field and tables
        var field, id, table;
        for(id in values){
            if(!Ext.isFunction(values[id]) && (field = this.findField(id))){
            	if (values[id]) {
	                field.setValue(values[id]);
	                if(this.trackResetOnLoad){
	            		field.originalValue = field.getValue();
	                }
            	}
            } else if(!Ext.isFunction(values[id]) && (table = this.findTable(id))){
                table.loadData(values[id]);
            }
        }
        return this;
    },
    
    /**
     * @private
     */
    infoMessage: function(msg) {
        this.fireEvent('infoMessage', msg, this);
    },
    
    isDirty : function(){
        var dirty = false;
        //Loop on fields
        this.items.each(function(f) {
            if (f.trackDirty != false && f.isDirty()) {
                dirty = true;
                return false;
            }
        });
        //Loop on Tables
        this.tables.each(function(table) {
            if (table.trackDirty != false && table.isDirty()) {
                dirty = true;
                return false;
            }
        });
        
        return dirty;
    },
    
    /**
     * @private
     */
    addTable : function(c) {
        this.tables.add(c.name || c.id , c);
        return this;
    },
    
    /**
     * @private
     */
    addComboBox : function(c) {
        this.comboBoxs.add(c.name || c.id , c);
        return this;
    },
    
    /**
     * @private
     * After saving/updating a form is successfully. Reset all original value for all Field and editable Table.
     */
    resetAfterSuccess : function() {
		//reset for Field
    	this.items.each(function(f){
    	    if (f instanceof Ext.form.CheckboxGroup || 
    	            (f instanceof Ext.form.CompositeField && typeof(f.getValue()) === 'undefined')) {
    	        f.items.each(function(item, index) {
    	            item.originalValue = item.getValue();
    	        });
    	    } else {
    	        f.originalValue = f.getValue();
    	    }
        });
    	//reset for Table
    	this.tables.each(function(f) {
    		this.store.modified = [];
    		this.originalSize = this.store.data.length;
    	});
    },
    
    initEl : function(el){
        this.el = Ext.get(el);
        this.id = this.el.id || Ext.id();
        if(!this.standardSubmit){
            this.el.on('submit', this.onSubmit, this);
        }
        this.el.addClass('x-form');
        
        //start customize : calculate the loading mask position to ensure it always display at center
        var c = this.el.findParent('div.content-panel-container');
        if(c) {
            this.el.applyStyles('height: ' + c.clientHeight + 'px;');
        }
        //end customize
    }
});
